package com.ly.framework.aspectj;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ly.common.annotation.Log;
import com.ly.common.core.domain.model.LoginUser;
import com.ly.common.enums.BusinessStatus;
import com.ly.common.utils.ServletUtils;
import com.ly.common.utils.StringUtils;
import com.ly.common.utils.erp.JsonUtil;
import com.ly.common.utils.ip.IpUtils;
import com.ly.common.utils.spring.SpringUtils;
import com.ly.framework.manager.AsyncManager;
import com.ly.framework.manager.factory.AsyncFactory;
import com.ly.framework.web.service.TokenService;
import com.ly.system.domain.SysOperLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import com.alibaba.fastjson.JSON;

/**
 * 操作日志记录处理
 *
 * @author ruoyi
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    // 配置织入点
    @Pointcut ( "@annotation(com.ly.common.annotation.Log)" )
    public void logPointCut () {
    }

    /**
     * 环绕切面，统计日志及接口效率，不入库
     * @param point
     * @return
     * @throws Throwable
     */
//    @Around ( "logPointCut()" )
//    public Object around ( ProceedingJoinPoint point ) throws Throwable {
//        ServletRequestAttributes attributes = ( ServletRequestAttributes ) RequestContextHolder.getRequestAttributes ();
//        HttpServletRequest request = attributes.getRequest ();
//        String operatorName = getOperatorName ( point ); // 加在切面的注解值
//        String url = request.getRequestURL ().toString ();
//        String reqParam = preHandle ( point );//JsonUtil.beanToJsonString(point.getArgs()[0]);
//        //获取方法
//        Method method = ( ( MethodSignature ) point.getSignature () ).getMethod ();
//        Class returnType = method.getReturnType ();
//        // 获取AspectAnnotation注解
//        Log aspectAnnotation = method.getAnnotation ( Log.class );
//        log.info ( "切面开始->{}-请求参数:[{}],请求URL:[{}]", operatorName, reqParam, url );
//        long begin = System.currentTimeMillis ();
//        Object result = point.proceed (); //反射执行具体方法
//        String respParam = postHandle ( result );
//        log.info ( "切面结束->{}-响应参数:[{}], 耗时：[{}ms]", operatorName, respParam, ( System.currentTimeMillis () - begin ) );
//        return result;
//    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning ( pointcut = "logPointCut()", returning = "jsonResult" )
    public void doAfterReturning ( JoinPoint joinPoint, Object jsonResult ) {
        handleLog ( joinPoint, null, jsonResult );
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing ( value = "logPointCut()", throwing = "e" )
    public void doAfterThrowing ( JoinPoint joinPoint, Exception e ) {
        handleLog ( joinPoint, e, null );
    }

    protected void handleLog ( final JoinPoint joinPoint, final Exception e, Object jsonResult ) {
        try {
            // 获得注解
            Log controllerLog = getAnnotationLog ( joinPoint );
            if ( controllerLog == null ) {
                return;
            }

            // 获取当前的用户
            LoginUser loginUser = SpringUtils.getBean ( TokenService.class ).getLoginUser ( ServletUtils.getRequest () );

            // *========数据库日志=========*//
            SysOperLog operLog = new SysOperLog ();
            operLog.setStatus ( BusinessStatus.SUCCESS.ordinal () );
            // 请求的地址
            String ip = IpUtils.getIpAddr ( ServletUtils.getRequest () );
            operLog.setOperIp ( ip );
            // 返回参数
            operLog.setJsonResult ( JSON.toJSONString ( jsonResult ) );

            operLog.setOperUrl ( ServletUtils.getRequest ().getRequestURI () );
            if ( loginUser != null ) {
                operLog.setOperName ( loginUser.getUsername () );
            }

            if ( e != null ) {
                operLog.setStatus ( BusinessStatus.FAIL.ordinal () );
                operLog.setErrorMsg ( StringUtils.substring ( e.getMessage (), 0, 2000 ) );
            }
            // 设置方法名称
            String className = joinPoint.getTarget ().getClass ().getName ();
            String methodName = joinPoint.getSignature ().getName ();
            operLog.setMethod ( className + "." + methodName + "()" );
            // 设置请求方式
            operLog.setRequestMethod ( ServletUtils.getRequest ().getMethod () );
            // 处理设置注解上的参数
            getControllerMethodDescription ( joinPoint, controllerLog, operLog );
            // 保存数据库
            AsyncManager.me ().execute ( AsyncFactory.recordOper ( operLog ) );
        } catch ( Exception exp ) {
            // 记录本地异常日志
            log.error ( "==前置通知异常==" );
            log.error ( "异常信息:{}", exp.getMessage () );
            exp.printStackTrace ();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log     日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription ( JoinPoint joinPoint, Log log, SysOperLog operLog ) throws Exception {
        // 设置action动作
        operLog.setBusinessType ( log.businessType ().ordinal () );
        // 设置标题
        operLog.setTitle ( log.title () );
        // 设置操作人类别
        operLog.setOperatorType ( log.operatorType ().ordinal () );
        // 是否需要保存request，参数和值
        if ( log.isSaveRequestData () ) {
            // 获取参数的信息，传入到数据库中。
            setRequestValue ( joinPoint, operLog );
        }
    }

    /**
     * 获取请求的参数，放到log中
     *
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue ( JoinPoint joinPoint, SysOperLog operLog ) throws Exception {
        String requestMethod = operLog.getRequestMethod ();
        if ( HttpMethod.PUT.name ().equals ( requestMethod ) || HttpMethod.POST.name ().equals ( requestMethod ) ) {
            String params = argsArrayToString ( joinPoint.getArgs () );
            operLog.setOperParam ( StringUtils.substring ( params, 0, 2000 ) );
        } else {
            Map<?, ?> paramsMap = ( Map<?, ?> ) ServletUtils.getRequest ().getAttribute ( HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE );
            operLog.setOperParam ( StringUtils.substring ( paramsMap.toString (), 0, 2000 ) );
        }
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private Log getAnnotationLog ( JoinPoint joinPoint ) throws Exception {
        Signature signature = joinPoint.getSignature ();
        MethodSignature methodSignature = ( MethodSignature ) signature;
        Method method = methodSignature.getMethod ();

        if ( method != null ) {
            return method.getAnnotation ( Log.class );
        }
        return null;
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString ( Object[] paramsArray ) {
        String params = "";
        if ( paramsArray != null && paramsArray.length > 0 ) {
            for ( int i = 0; i < paramsArray.length; i++ ) {
                if ( ! isFilterObject ( paramsArray[i] ) ) {
                    Object jsonObj = JSON.toJSON ( paramsArray[i] );
                    params += jsonObj.toString () + " ";
                }
            }
        }
        return params.trim ();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    public boolean isFilterObject ( final Object o ) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }


    /**
     * @Author: Wuzh
     * @Description: 获取自定义注解value值
     * @Data:
     */
//    private String getOperatorName ( ProceedingJoinPoint joinPoint ) {
//        Signature signature = joinPoint.getSignature ();
//        MethodSignature methodSignature = ( MethodSignature ) signature;
//        Method targetMethod = methodSignature.getMethod ();
//        Log operateName = targetMethod.getAnnotation ( Log.class );
//        return operateName.title ();
//    }
//
//
//    /**
//     * 入参数据
//     *
//     * @param joinPoint
//     * @return
//     */
//    private String preHandle ( ProceedingJoinPoint joinPoint ) {
//        String reqParam = "";
//        Signature signature = joinPoint.getSignature ();
//        MethodSignature methodSignature = ( MethodSignature ) signature;
//        Method targetMethod = methodSignature.getMethod ();
//        Annotation[] annotations = targetMethod.getAnnotations ();
//        for ( Annotation annotation : annotations ) {
//            if ( annotation.annotationType ().equals ( RequestMapping.class ) || annotation.annotationType ().equals ( GetMapping.class ) || annotation.annotationType ().equals ( PostMapping.class ) || annotation.annotationType ().equals ( PutMapping.class ) || annotation.annotationType ().equals ( DeleteMapping.class ) ) {
//                reqParam = JsonUtil.beanToJsonString ( joinPoint.getArgs () );
//                break;
//            }
//        }
//        return reqParam;
//    }
//
//
//    private String postHandle ( Object retVal ) throws IOException {
//        if ( null == retVal ) {
//            return "";
//        }
//        return JsonUtil.beanToJsonString ( retVal );
//    }


}
